#pragma once

#include "Error.h"
#include "NonCopyable.h"

namespace TestFramework
{
    template <class T>
    class ArrayProcessor : NonCopyable
    {
    public:
        virtual void ProcessItem(T& item) = 0;
    };

    template <class T>
    class ArrayComparer : NonCopyable
    {
    public:
        virtual bool Compare(const T& lhs, const T& rhs) const = 0;
    };

    template <class T, size_t Length>
    class TestArray
    {
    public:
        typedef T* Iterator;
        typedef const T* ConstIterator;
        typedef size_t SizeType;
        typedef T ValueType;
        typedef const T ConstValueType;
        
        TestArray()
            : Size(Length)
            , Array(new ValueType[Length])
        {
        }

        ~TestArray()
        {
            delete [] Array;
        }

        ValueType& operator[](SizeType i)
        {
            CheckRange(i);
            return Array[i];
        }

        ConstValueType& operator[](SizeType i) const
        {
            CheckRange(i);
            return Array[i];
        }

        void Enumerate(ArrayProcessor<ValueType>& processor)
        {
            for (Iterator iter = Begin(); iter != End(); ++iter)
            {
                processor.ProcessItem(*iter);
            }
        }

        void Sort(const ArrayComparer<ValueType>& comparer)
        {
            for (Iterator iter = Begin(); iter != (End() - 1); ++iter)
            {
                for (Iterator subIter = iter + 1; subIter != End(); ++subIter)
                {
                    if (comparer.Compare(*iter, *subIter))
                    {
                        ValueType value = *iter;
                        *iter = *subIter;
                        *subIter = value;
                    }
                }
            }
        }

        void Clear()
        {
            delete [] Array;
            Array = new ValueType[Size];
        }

        void Fill(ConstValueType& value)
        {
            for (Iterator iter = Begin(); iter != End(); ++iter)
            {
                *iter = value;
            }
        }

        SizeType GetSize() const
        {
            return Size;
        }

        Iterator Begin()
        {
            return &Array[0];
        }

        ConstIterator Begin() const
        {
            return &Array[0];
        }

        Iterator End()
        {
            return &Array[GetSize()];
        }

        ConstIterator End() const
        {
            return &Array[GetSize()];
        }

    private:
        void CheckRange(SizeType index) const
        {
            if (index < 0 || index >= Size)
            {
                throw std::exception("Out of range!", Errors::ERROR_OUT_OF_RANGE);
            }
        }

    private:
        const SizeType Size;
        ValueType* Array;
    };
}
